package org.mixed.script.reflection;

import org.mixed.script.functions.MixScriptFunctions;

import java.beans.Transient;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class JavaReflection {

    private static final List<JavaInvoker<Method>> FUNCTIONS = new ArrayList<>();

    static {
        registerFunction(new MixScriptFunctions());
    }

    public static void registerFunction(Object target) {
//        Stream.of(target.getClass().getMethods())
//                .filter(method -> method.isAnnotationPresent(Function.class))
//                .map(MethodInvoker::new)
//                .forEach(it -> {
//                    it.setDefaultTarget(target);
//                    FUNCTIONS.add(it);
//                });
    }

    public static JavaInvoker<Method> findMethodInvoker(List<JavaInvoker<Method>> methods, Class<?>[] parameterTypes) {
//        return findInvoker(methods, parameterTypes);
        return null;
    }

    public static JavaInvoker<Constructor> findConstructorInvoker(List<Constructor<?>> constructors, Class<?>[] parameterTypes) {
//        return findInvoker(constructors.stream().map(ConstructorInvoker::new).collect(Collectors.toList()), parameterTypes);
        return null;
    }

    public static JavaInvoker<Method> findInvoker(Class<?> cls, String name) {
        return findInvoker(cls, name, new Class<?>[0]);
    }
    public static JavaInvoker<Method> findInvoker(Class<?> cls, String name, Class<?>[] parameterTypes) {
        List<JavaInvoker<Method>> methodList = new ArrayList<>();
        Method[] methods = cls.getMethods();
        for (int i = 0, n = methods.length; i < n; i++) {
            Method method = methods[i];
            if (!method.getName().equals(name)) {
                continue;
            }
            if (method.getAnnotation(Transient.class) != null) {
                continue;
            }
            if (Modifier.isPublic(method.getModifiers())) {
                methodList.add(new MethodInvoker(method));
            }
        }
//        return findMethodInvoker(methodList, parameterTypes);
        return null;
    }

    public static <T extends Executable> JavaInvoker<T> findInvoker(List<JavaInvoker<T>> executables, Class<?>[] parameterTypes) {
//        JavaInvoker<T> foundInvoker = null;
//        int foundScore = 0;
//        List<JavaInvoker<T>> executableWithVarArgs = new ArrayList<>();
//        for (JavaInvoker<T> invoker : executables) {
//            // Check if the types match.
//            Class<?>[] otherTypes = invoker.getParameterTypes();
//            invoker = invoker.copy();
//            int score = matchTypes(invoker, parameterTypes, otherTypes, true);
//            if (score > -1) {
//                if (foundInvoker == null) {
//                    foundInvoker = invoker;
//                    foundScore = score;
//                } else {
//                    if (score < foundScore) {
//                        foundScore = score;
//                        foundInvoker = invoker;
//                    }
//                }
//            } else if (invoker.isVarArgs()) {
//                executableWithVarArgs.add(invoker);
//            }
//        }
//        if (foundInvoker == null) {
//            for (JavaInvoker<T> invoker : executableWithVarArgs) {
//                Class<?>[] otherTypes = invoker.getParameterTypes();
//                int score = -1;
//                int fixedParaLength = otherTypes.length - 1;
//                if (parameterTypes.length >= fixedParaLength) {
//                    Class<?>[] argTypes = new Class<?>[fixedParaLength];
//                    System.arraycopy(parameterTypes, 0, argTypes, 0, fixedParaLength);
//                    invoker = invoker.copy();
//                    score = matchTypes(invoker, argTypes, otherTypes, false);
//                    if (score > -1) {
//                        Class<?> target = otherTypes[fixedParaLength].getComponentType();
//                        for (int i = fixedParaLength; i < parameterTypes.length; i++) {
//                            Class<?> type = parameterTypes[i];
//                            if(RuntimeContext.class.isAssignableFrom(type)){
//                                score++;
//                            } else if (Null.class.equals(type)) {
//                                if (!target.isPrimitive()) {
//                                    score++;
//                                } else {
//                                    score = -1;
//                                    break;
//                                }
//                            } else if (!isPrimitiveAssignableFrom(type, target)) {
//                                score++;
//                                if (!target.isAssignableFrom(type)) {
//                                    score++;
//                                    if (!isCoercible(type, target)) {
//                                        boolean found = false;
//                                        for (ClassImplicitConvert convert : CONVERTS) {
//                                            if (convert.support(type, target)) {
//                                                invoker.addClassImplicitConvert(i, convert);
//                                                found = true;
//                                            }
//                                        }
//                                        invoker.setImplicit(found);
//                                        if (!found) {
//                                            score = -1;
//                                            break;
//                                        }
//                                        score++;
//                                    } else {
//                                        score++;
//                                    }
//                                }
//                            }
//                        }
//                    }
//                }
//                if (score > -1) {
//                    if (foundInvoker == null) {
//                        foundInvoker = invoker;
//                        foundScore = score;
//                    } else {
//                        if (score < foundScore) {
//                            foundScore = score;
//                            foundInvoker = invoker;
//                        }
//                    }
//                }
//            }
//        }
//        return foundInvoker;
        return null;
    }
}
